﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace AOC19NET.Controls {
	/// <summary>
	/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
	///
	/// Step 1a) Using this custom control in a XAML file that exists in the current project.
	/// Add this XmlNamespace attribute to the root element of the markup file where it is 
	/// to be used:
	///
	///     xmlns:MyNamespace="clr-namespace:AOC19NET.Controls"
	///
	///
	/// Step 1b) Using this custom control in a XAML file that exists in a different project.
	/// Add this XmlNamespace attribute to the root element of the markup file where it is 
	/// to be used:
	///
	///     xmlns:MyNamespace="clr-namespace:AOC19NET.Controls;assembly=AOC19.NET.Controls"
	///
	/// You will also need to add a project reference from the project where the XAML file lives
	/// to this project and Rebuild to avoid compilation errors:
	///
	///     Right click on the target project in the Solution Explorer and
	///     "Add Reference"->"Projects"->[Browse to and select this project]
	///
	///
	/// Step 2)
	/// Go ahead and use your control in the XAML file.
	///
	///     <!--<CustomControls:ScrollingTextBox/>-->
	///
	/// </summary>
	public class ScrollingTextBox : TextBox {
		/*-------------------------------------------------------------------------*/
		/* MEMBERS																   */
		/*-------------------------------------------------------------------------*/
		#region MEMBERS
		/// <summary>
		/// Text scroll speed, in pixels per <see cref="m_dScrollUpdateInterval"/> milliseconds.
		/// </summary>
		public double m_dScrollSpeed;

		/// <summary>
		/// Update interval of the scrolling in milliseconds.
		/// </summary>
		public double m_dScrollUpdateInterval;

		/// <summary>
		/// The time to wait before start scrolling to the opposite direction.
		/// </summary>
		public double m_dPauseBetweenScrollings;

		/// <summary>
		/// Timer to control the scrolling interval.
		/// </summary>
		private DispatcherTimer m_ScrollTimer;

		/// <summary>
		/// Scrolling direction.
		/// </summary>
		private int m_iScrollDir;

		/// <summary>
		/// Last offset value for the text box. This will help us check if the end
		/// has been reached and we must change the scrolling direction.
		/// </summary>
		private double m_dLastOffset;

		/// <summary>
		/// Scrolling timeout for the text box. This will keep track of the time to
		/// wait before start scrolling in the opposite direction.
		/// </summary>
		private TimeSpan m_ScrollTimeout;
		#endregion

		/*-------------------------------------------------------------------------*/
		/* METHODS																   */
		/*-------------------------------------------------------------------------*/
		#region METHODS
		/// <summary>
		/// Defaul constructor
		/// </summary>
		public ScrollingTextBox() {
			m_dScrollSpeed = 3.5;
			m_dScrollUpdateInterval = 250.0;
			m_dPauseBetweenScrollings = 2.0;
			m_iScrollDir = 1;
			m_dLastOffset = -1;
			m_ScrollTimeout = new TimeSpan(0);
			m_ScrollTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(m_dScrollUpdateInterval), DispatcherPriority.Render, UpdateScroll, Dispatcher.CurrentDispatcher);
		}

		/// <summary>
		/// To be called periodically. Will check each text box to see if it needs scrolling and, in case it needs it, update the scrolling position.
		/// </summary>
		/// <param name="_Sender">Event sender</param>
		/// <param name="_EventArgs">Event arguments</param>
		private void UpdateScroll(object _Sender, EventArgs _EventArgs) {
			string sActionTook = "NONE";
			if(ExtentWidth > ActualWidth) {
				// Obtain current and max offset (dirty)
				double dOffset = HorizontalOffset;
				//Console.WriteLine("Offset = {0} (Last = {1})", dOffset, m_dLastOffset);

				// Needs scrolling, check the direction
				// LEFT TO RIGHT
				if(m_iScrollDir == 1) {
					// Positive scrolling, check if the limit has been reached
					if(dOffset == m_dLastOffset) {
						// The string has reached the end, reset timeout and change direction
						m_ScrollTimeout = TimeSpan.FromSeconds(m_dPauseBetweenScrollings);
						m_iScrollDir = -1;
						m_dLastOffset = -1;
						sActionTook = "POSITIVE RESET AND CHANGE DIR";
					}

					// The limit hasn't been reach. If the timer has ended, advance.
					else if(m_ScrollTimeout.TotalSeconds <= 0) {
						ScrollToHorizontalOffset(dOffset + m_dScrollSpeed);
						m_dLastOffset = dOffset;
						sActionTook = "POSITIVE ADVANCE";
					}

					// The limit hasn't been reach but the timer is running. Update the timer and do nothing else.
					else {
						m_ScrollTimeout -= TimeSpan.FromMilliseconds(m_dScrollUpdateInterval);
						sActionTook = "POSITIVE COUNTDOWN";
					}
				}

				// RIGHT TO LEFT
				else {
					// Negative scrolling, check if the limit has been reached
					if(dOffset == m_dLastOffset) {
						// The string has reached the limit, reset timeout and change direction
						m_ScrollTimeout = TimeSpan.FromSeconds(m_dPauseBetweenScrollings);
						m_iScrollDir = 1;
						m_dLastOffset = -1;
						sActionTook = "NEGATIVE RESET AND CHANGE DIR";
					}

					// The limit hasn't been reach. If the timer has ended, advance.
					else if(m_ScrollTimeout.TotalSeconds <= 0) {
						ScrollToHorizontalOffset(dOffset - m_dScrollSpeed);
						m_dLastOffset = dOffset;
						sActionTook = "NEGATIVE ADVANCE";
					}

					// The limit hasn't been reach but the timer is running. Update the timer and do nothing else.
					else {
						m_ScrollTimeout -= TimeSpan.FromMilliseconds(m_dScrollUpdateInterval);
						sActionTook = "NEGATIVE COUNTDOWN";
					}
				}
			}

			// Debug
			//	Console.WriteLine("\tDirection = {0}", m_iScrollDir);
			//	Console.WriteLine("\tTimer     = {0}", m_ScrollTimeout.TotalSeconds);
			//	Console.WriteLine("\tAction    = {0}", sActionTook);
			//	PrintProperties();
		}

		/// <summary>
		/// Print some properties of the current TextBox. Debug Purposes.
		/// </summary>
		public void PrintProperties() {
			Console.WriteLine("{0}", Name);
			Console.WriteLine("\tWidth       : {0}", Width);
			Console.WriteLine("\tActual Width: {0}", ActualWidth);
			Console.WriteLine("\tExtent Width: {0}", ExtentWidth);
			Console.WriteLine("\tMargin		 : {0}", Margin);
		}
		#endregion
	}
}
